package com.xprophet.crs.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.xprophet.crs.dao.JPEGFingerprintMapper;
import com.xprophet.crs.jpeg.codec.FingerprintType;
import com.xprophet.crs.jpeg.codec.IJPEGDecoder;
import com.xprophet.crs.jpeg.codec.impl.ExifDecoderImpl;
import com.xprophet.crs.jpeg.codec.model.Fingerprint;

@Service
public class JPEGFingerprintService {

	private static Logger log = Logger.getLogger(JPEGFingerprintService.class); 
	@Autowired
	private JPEGFingerprintMapper fpMapper;

	@Transactional
	public void insertFingerprints(List<Fingerprint> fingerprintList) {
		for (Fingerprint fp : fingerprintList) {
			try {
				fpMapper.insertFingerprint(fp);
			} catch (DataAccessException e) {
				log.debug("", e);
			}
		}
		//TODO: if fp count is very large, need insert by batch
	}

	@Transactional
	public Fingerprint getMatchedFingerprint(List<IJPEGDecoder> codecs, 
			List<String> filters, File jpegFile) {
		Fingerprint retval = null;
		if (codecs == null) {
			return retval;
		}
		
		// get exif from meta-data
		Fingerprint exif = null;
		ExifDecoderImpl exifCodec = new ExifDecoderImpl();
		InputStream is = null;
		try {
			is = new FileInputStream(jpegFile);
			if (exifCodec.parseImage(is)) {
				exif = exifCodec.getExif();
			}
		} catch (FileNotFoundException e) {
			log.debug("", e);
		}
		
		// apply codecs to fetch matched fingerprint
		Iterator<IJPEGDecoder> it = codecs.iterator();
		while (it.hasNext()) {
			IJPEGDecoder codec = it.next();
			if (filters.contains(codec.getID())) {
				continue; // omit this codec
			}
			try {
				is = new FileInputStream(jpegFile);
				if (codec.parseImage(is)) {
					// compare encrypted jpeg DQTs in database
					String key = codec.getEncryptedData();
					
					// query database
					codec.setExif(exif);
					retval = codec.getMatchResult(key, retval);
					int type = retval.getType();

					log.info("[" + codec.getID() + "]" + retval);
					if (FingerprintType.NONE == type) {
//						if (!it.hasNext()) {
						//TODO: ADD MORE FIELDS
						if (StringUtils.isNotBlank(retval.getMake())) {
							retval.setType(FingerprintType.HARDWARE);
						} else if (StringUtils.isNotBlank(retval.getEditor())) {
							retval.setType(FingerprintType.SOFTWARE);
							retval.setMake(retval.getEditor());
						} else {
							continue;
						}
						try {
							fpMapper.insertFingerprint(retval);
							codec.updateMatchMap(retval);
						} catch (DataAccessException e) {
							log.debug("", e);
						}
//						}
					} else if (FingerprintType.SOFTWARE == type) {
						if (!it.hasNext()) {
							try {
								fpMapper.updateFingerprintLite(retval);
								codec.updateMatchMap(retval);
							} catch (DataAccessException e) {
								log.debug("", e);
							}
						}
						break;
					} else if (FingerprintType.HARDWARE == type) {
						if (!it.hasNext()) {
							try {
								fpMapper.updateFingerprintLite(retval);
								codec.updateMatchMap(retval);
							} catch (DataAccessException e) {
								log.debug("", e);
							}
						}
						break;
					}
				}
			} catch (FileNotFoundException e) {
				log.debug("", e);
				break;
			}
		}
		return retval;
	}

	@Transactional
	public List<Fingerprint> getAllFingerprints() {
		return fpMapper.getFingerprints();
	}
	
	@Transactional
	public List<Fingerprint> getAllFingerprintsLite() {
		return fpMapper.getFingerprintsLite();
	}

}
